package com.example.springboottest.example.io.redirect.appserver.nio2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.concurrent.TimeUnit;

/**
 * nio2对nio的功能改进，最重要是对异步I/O即AIO的支持
 * 1、通道      和nio类似，但改为AsynchronousSocketChannel，继承Channel
 * 2、缓冲区    和nio类似
 * 3、AIO的有两种实现方式  Future和CompletionHandler
 * Future 需要程序自己监测I/O状态或者通过Feture.get()等待I/O操作结束
 * CompletionHandler 则由JDK监测I/O状态，程序只需要实现每种状态的处理即可
 * <p>
 * <p>
 * 本例子主要针对CompletionHandler和Future混合实现
 * <p>
 * 其中ClientCompletionHandler的实现完全异步，不阻塞当前线程
 * 与NIO2Client不同，那个future.get()会阻塞线程
 */
public class NIO2Client2 {
    class ClientCompletionHandler implements CompletionHandler<Void, Void> {
        private AsynchronousSocketChannel channel;
        private CharBuffer charBuffer;
        private CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
        private BufferedReader clientInput = new BufferedReader(new InputStreamReader(System.in));

        public ClientCompletionHandler(AsynchronousSocketChannel channel) {
            this.channel = channel;
        }

        @Override
        public void completed(Void result, Void attachment) {
            try {
                System.out.println("输入客户端请求");
                String request = clientInput.readLine();
                //发送客户端请求
                channel.write(ByteBuffer.wrap(request.getBytes())).get();
                //创建读取缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //读取服务端响应
                while (channel.read(buffer).get() != -1) {
                    buffer.flip();
                    charBuffer = decoder.decode(buffer);
                    String response = charBuffer.toString().trim();
                    System.out.println("服务端响应" + response);
                    if (buffer.hasRemaining())
                        buffer.compact();
                    else
                        buffer.clear();
                    //读取并发送下一次请求
                    request = clientInput.readLine();
                    channel.write(ByteBuffer.wrap(request.getBytes())).get();
                }
            } catch (Exception ex) {

            } finally {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void failed(Throwable exc, Void attachment) {
            throw new RuntimeException("链接服务器失败");
        }
    }

    public void start() throws Exception {
        //创建异步Socket通道
        AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
        if (channel.isOpen()) {
            channel.setOption(StandardSocketOptions.SO_RCVBUF, 128 * 1024);
            channel.setOption(StandardSocketOptions.SO_SNDBUF, 128 * 1024);
            channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
            //链接服务器，指定CompletionHandler
            channel.connect(new InetSocketAddress("127.0.0.1", 8080), null, new ClientCompletionHandler(channel));
            //主线程等待
            while (true)
                TimeUnit.MINUTES.sleep(5);
        } else
            throw new RuntimeException("异步通道未打开");
    }

    public static void main(String[] args) throws Exception {
        NIO2Client2 client = new NIO2Client2();
        client.start();
    }
}
